<template>
	<el-table ref="tableRef" v-loading="_loading" :data="tableData" :max-height="maxHeight!" :row-style="handleRowStyle" border class="cus-table" fit
	          highlight-current-row>

		<el-table-column v-if="selection" :width="50" align="center" header-align="center">
			<template #header>
				<el-checkbox v-model="rootChecked" :indeterminate="rootIndeterminate" @change="handleRootCheckboxChange"/>
			</template>
			<template v-slot="scope">
				<el-checkbox v-model="scope.row._checked" :indeterminate="scope.row._indeterminate" @change="handleCheckboxChange(scope.row._checked, scope.row)"/>
			</template>
		</el-table-column>

		<slot name="pre-column"/>

		<!--<el-table-column v-if="draggable" fixed="left" show-overflow-tooltip width="42">-->
		<!--	<icon class="drag" name="fa fa-arrows" size="18"/>-->
		<!--</el-table-column>-->

		<el-table-column
				v-for="column in columns"
				:key="column.key"
				:align="column.align || 'center'"
				:fixed="column.fixed ?? false"
				:header-align="column.headerAlign"
				:label="column.label"
				:min-width="column.minWidth ?? 0"
				:show-overflow-tooltip="column.showOverflowTooltip ?? false"
		>
			<template v-slot="scope">
				<slot :name="column.key + '_prefix'" :scope="scope"/>
				<template v-if="column.expandable">
					<span :style="`display:inline-block;height:10px;width:${scope.row._level*indent}px;`"/>
					<span v-if="showExpandIcon(scope.row)" style="margin-right: 5px;cursor: pointer"
					      @click="toggleExpanded(scope.row)">
						<icon v-if="!scope.row._expand" name="el-icon-CaretRight" size="18"/>
						<icon v-else name="el-icon-CaretBottom" size="18"/>
          </span>
					<span v-else style="margin-right: 23px"/>
				</template>
				<template v-if="column.iconClassFunction && column.iconClassFunction(scope.row)">
					<icon :name="column.iconClassFunction(scope.row)!" size="18" style="margin-right: 5px"/>
				</template>
				<slot :name="column.key" :scope="scope">
					{{ scope.row[column.key] }}
				</slot>
				<slot :name="column.key + '_suffix'" :scope="scope" style="margin-left: 5px"/>
			</template>
		</el-table-column>

	</el-table>
</template>

<script lang="ts" setup>
import {voTreeToRowList} from '@/utils/treeTableUtil'
import {includes} from 'lodash-es'
import {computed, type CSSProperties, ref, watch} from 'vue'
import {treeArrayIteratorUp} from '@/utils/commonUtil'
import Icon from '@/components/icon.vue'
import type {TableInstance} from 'element-plus'

interface Props {
	data: AuthorityVo[]
	columns: ColumnsOption[]
	// draggable?: boolean
	expandAll?: boolean
	defaultExpandAll?: boolean
	indent?: number
	selection?: boolean
	loading?: boolean
	maxHeight?: number
}

const props = withDefaults(defineProps<Props>(), {
	// draggable: false,
	expandAll: false,
	defaultExpandAll: false,
	indent: 25,
	selection: false,
	loading: false
})

watch(() => props.expandAll, value => {
	handleExpandAll(value)
})

const emits = defineEmits<{
	(e: 'tree-table-selection-change', value: AuthorityRow[]): void
	(e: 'update:loading', value: boolean): void
	(e: 'update:expandAll', value: boolean): void
}>()

const tableRef = ref<TableInstance>()
const rootIndeterminate = ref(false)
const rootChecked = ref(false)

const _loading = computed({
	get: () => props.loading,
	set: (val) => {
		emits('update:loading', val)
	}
})

const tableData = computed<AuthorityRow[]>(() => {
	if (props.data.length === 0) {
		return []
	}
	_loading.value = true
	const _tableData = voTreeToRowList(props.data, {
		_expand: props.defaultExpandAll
	})
	_loading.value = false
	return _tableData
})

watch(tableData, value => {
	let total = 0
	let expandCount = 0
	let unExpandCount = 0
	for (const row of value) {
		if (row.children && row.children.length > 0) {
			++total
			if (row._expand) {
				++expandCount
			} else {
				++unExpandCount
			}
		}
	}
	if (unExpandCount === total) {
		emits('update:expandAll', false)
	} else if (expandCount === total) {
		emits('update:expandAll', true)
	}
}, {deep: true})

const handleRowStyle = (obj: { row: AuthorityRow, rowIndex: number }) => {
	const row = obj.row
	const styleObject: CSSProperties = {}
	// 处理是否显示_show
	const parent = row._parent
	row._show = parent ? parent._expand && parent._show : true
	if (row._show) {
		styleObject.animation = 'fade-in 0.3s'
	} else {
		styleObject.display = 'none'
	}
	return styleObject
}

const showExpandIcon = (row: AuthorityRow) => {
	return row.children && row.children.length > 0
}

const toggleExpanded = (row: AuthorityRow) => {
	row._expand = !row._expand
}

/**
 * 处理 rootCheckbox 选中状态：checked 和 indeterminate
 */
const handleStatusForRootCheckbox = () => {
	let checkedCountForRoot = 0
	tableData.value.forEach(data => {
		if (data._checked) {
			checkedCountForRoot++
		}
	})
	rootChecked.value = checkedCountForRoot === tableData.value.length
	rootIndeterminate.value = checkedCountForRoot > 0 && checkedCountForRoot < tableData.value.length
}

const handleRootCheckboxChange = (val: boolean) => {
	_loading.value = true
	rootChecked.value = val
	rootIndeterminate.value = false
	tableData.value.forEach(data => {
		data._checked = val
		data._indeterminate = false
	})
	// 向父组件返回改变前后的数据集合
	emits('tree-table-selection-change', val ? tableData.value : [])
	_loading.value = false
}

const handleCheckboxChange = (val: boolean, row: AuthorityRow, notEmit?: boolean, notHandleRoot?: boolean) => {
	_loading.value = true
	row._indeterminate = false
	// 递归处理所有下级节点的选中状态和半选状态
	const downChangedFlag = checkRecursionDown(row, val)
	// 递归处理所有上级节点的选中状态和半选状态
	const upChangedFlag = checkRecursionUp(row, val)
	// 处理 rootCheckbox 选中状态和半选状态
	if (!notHandleRoot) {
		handleStatusForRootCheckbox()
	}
	// 向父组件返回改变前后的数据集合
	if (!notEmit) {
		emits('tree-table-selection-change', tableData.value.filter(data => data._checked))
	}
	_loading.value = false
	return downChangedFlag || upChangedFlag
}

/**
 * 递归处理所有下级节点的选中状态和半选状态
 * @param row
 * @param checked
 * @return {boolean} 是否有节点的选中状态发生了改变
 */
const checkRecursionDown = (row: AuthorityRow, checked: boolean): boolean => {
	let changedFlag = false
	const children = row.children
	if (children && children.length > 0) {
		for (const child of children) {
			if (child._checked !== checked) {
				changedFlag = true
			}
			child._checked = checked
			child._indeterminate = false
			const innerChangedFlag = checkRecursionDown(child, checked)
			if (innerChangedFlag) {
				changedFlag = innerChangedFlag
			}
		}
	}
	return changedFlag
}

/**
 * 递归处理所有上级节点的选中状态和半选状态
 * @param row
 * @param checked
 */
const checkRecursionUp = (row: AuthorityRow, checked: boolean): boolean => {
	let changedFlag = false
	if (row._parent) {
		let checkedBrotherCount = 0
		let indeterminateBrotherCount = 0
		for (const brother of row._parent.children) {
			if (brother._checked) {
				checkedBrotherCount++
			}
			if (brother._indeterminate) {
				indeterminateBrotherCount++
			}
		}
		const newChecked = checkedBrotherCount > 0
		if (row._parent._checked !== newChecked) {
			changedFlag = true
		}
		row._parent._checked = newChecked
		row._parent._indeterminate = indeterminateBrotherCount > 0 || newChecked && checkedBrotherCount < row._parent.children.length
		const innerChangedFlag = checkRecursionUp(row._parent, checked)
		if (innerChangedFlag) {
			changedFlag = innerChangedFlag
		}
	}
	return changedFlag
}

const handleExpandAll = (flag: boolean) => {
	tableData.value.forEach(row => {
		if (row.children && row.children.length > 0) {
			row._expand = flag
		}
	})
}

/**
 * 找到所有同级节点，包括自己
 * @param row
 */
const findAllBrother = (row: AuthorityRow) => {
	if (!row._parent) {
		return tableData.value.filter(item => item._level === 0)
	}
	return row._parent.children
}

/* 暴露的方法-start */

/**
 * 获取所有行数据
 */
const getAllRow = () => {
	return tableData.value
}

/**
 * 设置选中的行
 * @param dataIdList
 */
const setSelection = (dataIdList: number[]) => {
	_loading.value = true
	const checkedLeafNodeList: AuthorityRow[] = []
	let changedFlag = false
	// 处理所有节点的选中状态：checked
	for (const row of tableData.value) {
		row._indeterminate = false
		if (includes(dataIdList, row.id)) {
			if (row._checked === false) {
				changedFlag = true
			}
			row._checked = true
			if (!row.children || row.children.length === 0) {
				checkedLeafNodeList.push(row)
			}
		} else {
			if (row._checked === true) {
				changedFlag = true
			}
			row._checked = false
		}
	}
	for (const checkedLeafNode of checkedLeafNodeList) {
		const innerChangedFlag = handleCheckboxChange(true, checkedLeafNode, true, true)
		if (innerChangedFlag) {
			changedFlag = innerChangedFlag
		}
	}
	// 处理 rootCheckbox 选中状态和半选状态
	handleStatusForRootCheckbox()
	// 有些节点新增了下级，导致不再是叶子结点，找出这些节点和所有上级节点，变为半选状态
	const wrongNodeArray = []
	for (const data of tableData.value) {
		if (data._checked && !data._indeterminate && data.children && data.children.length > 0) {
			for (const child of data.children) {
				if (!child._checked) {
					wrongNodeArray.push(data)
					break
				}
			}
		}
	}
	for (const node of wrongNodeArray) {
		treeArrayIteratorUp(node, node => node._indeterminate = true, '_parent')
	}
	// 向父组件返回改变后的数据集合
	if (changedFlag) {
		emits('tree-table-selection-change', tableData.value.filter(data => data._checked))
	}
	_loading.value = false
}

/* 暴露的方法-end */

defineExpose({
	getAllRow,
	setSelection
})

// onMounted(() => {
// 	// 拖拽排序
// 	const el = tableRef.value!.$el.querySelector('.el-table__body-wrapper tbody')
// 	Sortable.create(el, {
// 		animation: 150,
// 		handle: '.drag',
// 		onStart: (event) => {
// 			// console.log('onStart', event)
// 		},
// 		onEnd: (event) => {
// 			// console.log('onEnd', event)
// 			if (exist(event.oldIndex) && exist(event.newIndex) && event.oldIndex !== event.newIndex) {
// 				// const row = toRaw(tableData.value!.splice(event.oldIndex!, 1)[0])
// 				// tableData.value!.splice(event.newIndex!, 0, row)
//
// 				// findAllBrother(tableData.value[event.oldIndex!])
//
// 				// const row = toRaw(userTableData.value.splice(event.oldIndex, 1)[0])
// 				// userTableData.value.splice(event.newIndex, 0, row)
// 			}
// 		}
// 	})
// })

</script>

<style lang="scss" scoped>
// 行hover效果
//:deep(.el-table__body tr:hover > td) {
//	 background-color: #232323;
//}

// 选中当前行效果
//:deep(.el-table__body tr.current-row > td) {
//	 background-color: #28282d;
//}

.drag {
	cursor: grab;
}
</style>
